Java, C, C++, Kotlin에서의 얕은 복사(Shallow Copy) vs 깊은 복사(Deep Copy) 가이드

객체나 참조형 자료구조를 다룰 때 얕은 복사깊은 복사의 차이를 이해하는 것은 버그를 방지하고 의도한 동작을 구현하는 데 매우 중요합니다. 아래는 해당 개념과 각 언어(Java, C, C++, Kotlin)에서의 자세한 설명과 예제를 포함한 비교입니다.

1. 정의

2. Java

👉 얕은 복사

예제:

class Address {
    String city;
    Address(String city) { this.city = city; }
}

class Person implements Cloneable {
    String name;
    Address address;
    public Person(String name, Address address) { this.name = name; this.address = address; }

    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 얕은 복사
    }
}

Person p1 = new Person("Alice", new Address("Seoul"));
Person p2 = (Person) p1.clone();

p1.address.city = "Busan";
System.out.println(p2.address.city); // 출력: Busan (p1과 p2가 같은 Address를 참조)

✅ 깊은 복사

class Person implements Cloneable {
    String name;
    Address address;

    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone();
        cloned.address = new Address(this.address.city); // 깊은 복사
        return cloned;
    }
}

3. C

👉 얕은 복사

typedef struct {
    char *city;
} Address;

typedef struct {
    char *name;
    Address *address;
} Person;

Person p1 = { "Alice", malloc(sizeof(Address)) };
p1.address->city = strdup("Seoul");

Person p2 = p1; // 얕은 복사

strcpy(p1.address->city, "Busan");
printf("%s\n", p2.address->city); // 출력: Busan (동일한 주소 참조)

✅ 깊은 복사

Person deepCopyPerson(const Person *src) {
    Person dest;
    dest.name = strdup(src->name);
    dest.address = malloc(sizeof(Address));
    dest.address->city = strdup(src->address->city);
    return dest;
}

4. C++

👉 얕은 복사

struct Address {
    std::string city;
};

struct Person {
    std::string name;
    Address *address;
};

Person p1 = {"Alice", new Address{"Seoul"}};
Person p2 = p1; // 얕은 복사

p1.address->city = "Busan";
std::cout << p2.address->city << std::endl; // 출력: Busan

✅ 깊은 복사

struct Person {
    std::string name;
    Address *address;

    Person(const Person& other) {
        name = other.name;
        address = new Address(*other.address); // 깊은 복사
    }

    Person& operator=(const Person& other) {
        if (this != &other) {
            delete address;
            address = new Address(*other.address);
            name = other.name;
        }
        return *this;
    }
};

5. Kotlin

👉 얕은 복사

data class Address(var city: String)
data class Person(val name: String, val address: Address)

val p1 = Person("Alice", Address("Seoul"))
val p2 = p1.copy()

p1.address.city = "Busan"
println(p2.address.city) // 출력: Busan (같은 Address 인스턴스를 참조)

✅ 깊은 복사

fun Person.deepCopy() = Person(name, Address(address.city))

val p2 = p1.deepCopy()
p1.address.city = "Busan"
println(p2.address.city) // 출력: Seoul

✅ 요약 비교 표

항목 얕은 복사 (Shallow Copy) 깊은 복사 (Deep Copy)
복사 대상 최상위 필드 복사, 참조는 그대로 모든 필드 및 참조 객체까지 깊숙이 재복사
내부 객체 공유 예 (참조 공유) 아니오 (독립된 복사본)
구현 방법 기본 복사 생성자, 대입 연산자, clone(), copy() 재귀적 복사 또는 수동 구현 필요
주의사항 변경 시 원본/복사본 모두 영향을 미침 구현이 복잡할 수 있고 성능에 부담 있음

💡 실전 팁